
package com.zhike.utils.file.httpfile;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;

import lombok.extern.slf4j.Slf4j;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zhike.common.constant.SysConfigConstants;
import com.zhike.service.file.UploaderRoot;
import com.zhike.service.system.ISystemConfigService;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.tika.Tika;
import org.apache.tika.exception.TikaException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@Component
public class FileUpload {
	public final static String SUCESS_KEY_NAME = "success";

	@Autowired
	private ISystemConfigService systemConfigService;

	/**
	 * 文件上传处理实现方法。
	 * 
	 * @param request
	 * @param response
	 * @param saveToDir        文件将要保存到的目录
	 * @param THRESHOLD_SIZE   磁盘临时缓存文件阀值。
	 * 
	 *                         <pre>
	 *         Apache文件上传组件在解析上传数据中的每个字段内容时，需要临时保存解析出的数据，以
	 *     便在后面进行数据的进一步处理（保存在磁盘特定位置或插入数据库）。因为Java虚拟机默
	 *     认可以使用的内存空间是有限的，超出限制时将会抛出“java.lang.OutOfMemoryError”错
	 *     误。如果上传的文件很大，例如800M的文件，在内存中将无法临时保存该文件内容，Apache
	 *     文件上传组件转而采用临时文件来保存这些数据；但如果上传的文件很小，例如600个字节的
	 *     文件，显然将其直接保存在内存中性能会更加好些。
	 * 
	 *         setSizeThreshold方法用于设置是否将上传文件已临时文件的形式保存在磁盘的临界值（以
	 *     字节为单位的int值），如果从没有调用该方法设置此临界值，将会采用系统默认值10KB。对
	 *     应的getSizeThreshold() 方法用来获取此临界值。
	 *                         </pre>
	 * 
	 * @param MAX_FILE_SIZE    单个文件最大大小
	 * @param MAX_REQUEST_SIZE 所有文件总大小（因为本方法支持多文件上传）
	 * @return 从Multipart Content中取出客户端传过来的参数
	 * @throws Exception
	 */
	public HashMap<String, Object> processFileUpload2(UploaderRoot uploader, MultipartFile file, String fileName,
			int THRESHOLD_SIZE, int MAX_FILE_SIZE, int MAX_REQUEST_SIZE) throws Exception {

		HashMap<String, Object> parametersFromClient = new HashMap<String, Object>();

		parametersFromClient.put(SUCESS_KEY_NAME, Boolean.FALSE);
		parametersFromClient.put("file_name", fileName);

		if (uploader.getFileSaveDir() == null)
			throw new Exception("[HTTP文件上传] saveToDir == null!");

		if (StringUtils.isEmpty(file.getOriginalFilename())) {
			log.error("[HTTP文件上传] 没有需要上传的文件，ServletFileUpload.isMultipartContent(request)!=true!");
			throw new IOException("[HTTP文件上传] Not file to upload!");
		}

		if (!this.validateFileTypeByTika(uploader, file)) {
			parametersFromClient.put("error", "不支持的文件类型");
			return parametersFromClient;
		}

		String uploadPath = uploader.getFileSaveDir();
		String loc = uploader.getPathTag();

		File uploadDir = new File(uploadPath + loc);
		if (!uploadDir.exists()) {
			uploadDir.mkdirs();
		}

		String filePath = uploadPath + loc + File.separator + fileName;
		File storeFile = new File(filePath);

		DiskFileItemFactory factory = new DiskFileItemFactory();
		factory.setSizeThreshold(THRESHOLD_SIZE);
		factory.setRepository(storeFile);

		ServletFileUpload upload = new ServletFileUpload(factory);
		upload.setFileSizeMax(MAX_FILE_SIZE);
		upload.setSizeMax(MAX_REQUEST_SIZE);

		try {
			file.transferTo(storeFile);
			parametersFromClient.put("url", systemConfigService.getValueByKey(SysConfigConstants.CONFIG_TYPE_IM,
					SysConfigConstants.CONFIG_FILE_DOMAIN) + loc + fileName);
			parametersFromClient.put(SUCESS_KEY_NAME, Boolean.TRUE);
		} catch (Exception ex) {
			log.error("[HTTP文件上传] 处理文件上传时出错了，" + ex.getMessage(), ex);
			throw ex;
		}
		return parametersFromClient;
	}

	public boolean validateFileTypeByTika(UploaderRoot uploader, MultipartFile file) throws IOException, TikaException {
		Tika tika = new Tika();
		String detectedType = tika.detect(file.getInputStream());
		Iterator<String> iterator = uploader.getDetectedTypes().iterator();
		while (iterator.hasNext()) {
			String ext = iterator.next();
			if (ext.equals(detectedType)) {
				return true;
			}
		}
		log.warn("文件上传 不支持的文件类型: {}", detectedType);
		return false;
	}
}
